package cn.snmp.mutual.org.opennms.protocols.snmp;

import java.util.*;

import cn.snmp.mutual.org.opennms.protocols.snmp.asn1.AsnDecodingException;
import cn.snmp.mutual.org.opennms.protocols.snmp.asn1.AsnEncoder;
import cn.snmp.mutual.org.opennms.protocols.snmp.asn1.AsnEncodingException;


public abstract class SnmpPduPacket implements SnmpSyntax, Cloneable {
    private static int sm_seq;
    private int m_command;
    private int m_requestId;
    private ArrayList m_variables;
    protected int m_errStatus;
    protected int m_errIndex;
    public static final int GET = 160;
    public static final int GETNEXT = 161;
    public static final int RESPONSE = 162;
    public static final int SET = 163;
    public static final int GETBULK = 165;
    public static final int INFORM = 166;
    public static final int V2TRAP = 167;
    public static final int REPORT = 168;
    public static final int ErrNoError = 0;
    public static final int ErrTooBig = 1;
    public static final int ErrNoSuchName = 2;
    public static final int ErrBadValue = 3;
    public static final int ErrReadOnly = 4;
    public static final int ErrGenError = 5;
    public static final int ErrNoAccess = 6;
    public static final int ErrWrongType = 7;
    public static final int ErrWrongLength = 8;
    public static final int ErrWrongEncoding = 9;
    public static final int ErrWrongValue = 10;
    public static final int ErrNoCreation = 11;
    public static final int ErrInconsistentValue = 12;
    public static final int ErrResourceUnavailable = 13;
    public static final int ErrCommitFailed = 14;
    public static final int ErrUndoFailed = 15;
    public static final int ErrAuthorizationError = 16;
    public static final int ErrNotWritable = 17;
    public static final int ErrInconsistentName = 18;

    protected SnmpPduPacket() {
        this.m_command = 0;
        this.m_requestId = 0;
        this.m_errStatus = 0;
        this.m_errIndex = 0;
        this.m_variables = new ArrayList();
    }

    protected SnmpPduPacket(final SnmpPduPacket second) {
        this.m_command = second.m_command;
        this.m_requestId = second.m_requestId;
        this.m_errStatus = second.m_errStatus;
        this.m_errIndex = second.m_errIndex;
        final int sz = second.m_variables.size();
        this.m_variables = new ArrayList(sz);
        for (int x = 0; x < sz; ++x) {
            this.m_variables.add(x, ((SnmpVarBind) second.m_variables.get(x)).duplicate());
        }
    }

    protected SnmpPduPacket(final int command) {
        this();
        this.m_command = command;
    }

    protected SnmpPduPacket(final int command, final SnmpVarBind[] vars) {
        this(command);
        if (vars != null) {
            this.m_variables.ensureCapacity(vars.length);
            for (int x = 0; x < vars.length; ++x) {
                this.m_variables.add(vars[x].duplicate());
            }
        }
    }

    public static synchronized int nextSequence() {
        if (SnmpPduPacket.sm_seq == 0) {
            final Date seed = new Date();
            final Random rnd = new Random(seed.getTime());
            SnmpPduPacket.sm_seq = rnd.nextInt();
        }
        return SnmpPduPacket.sm_seq++;
    }

    public int getCommand() {
        return this.m_command;
    }

    public void setCommand(final int cmd) {
        this.m_command = cmd;
    }

    public int getRequestId() {
        return this.m_requestId;
    }

    public void setRequestId(final int reqid) {
        this.m_requestId = reqid;
    }

    public int getLength() {
        return this.m_variables.size();
    }

    public void addVarBind(final SnmpVarBind vb) {
        this.m_variables.add(vb);
    }

    public void addVarBindAt(final int ndx, final SnmpVarBind vb) {
        this.m_variables.add(ndx, vb);
    }

    public SnmpVarBind getVarBindAt(final int ndx) {
        return (SnmpVarBind) this.m_variables.get(ndx);
    }

    public void setVarBindAt(final int ndx, final SnmpVarBind vb) {
        this.m_variables.set(ndx, vb);
    }

    public SnmpVarBind removeVarBindAt(final int ndx) {
        return (SnmpVarBind) this.m_variables.remove(ndx);
    }

    public SnmpVarBind[] toVarBindArray() {
        final Object[] list = this.m_variables.toArray();
        final SnmpVarBind[] vblist = new SnmpVarBind[list.length];
        for (int x = 0; x < list.length; ++x) {
            vblist[x] = (SnmpVarBind) list[x];
        }
        return vblist;
    }

    public byte typeId() {
        return (byte) (this.m_command & 0xFF);
    }

    public int encodeASN(final byte[] buf, int offset, final AsnEncoder encoder) throws AsnEncodingException {
        final int begin = offset;
        final SnmpInt32 val = new SnmpInt32(this.m_requestId);
        offset = val.encodeASN(buf, offset, encoder);
        val.setValue(this.m_errStatus);
        offset = val.encodeASN(buf, offset, encoder);
        val.setValue(this.m_errIndex);
        final int vbbegin;
        offset = (vbbegin = val.encodeASN(buf, offset, encoder));
        for (int sz = this.m_variables.size(), x = 0; x < sz; ++x) {
            final SnmpVarBind ref = (SnmpVarBind) this.m_variables.get(x);
            offset = ref.encodeASN(buf, offset, encoder);
        }
        int pivot = offset;
        offset = encoder.buildHeader(buf, offset, (byte) 48, pivot - vbbegin);
        SnmpUtil.rotate(buf, vbbegin, pivot, offset);
        pivot = offset;
        offset = encoder.buildHeader(buf, offset, this.typeId(), pivot - begin);
        SnmpUtil.rotate(buf, begin, pivot, offset);
        return offset;
    }

    public int decodeASN(final byte[] buf, int offset, final AsnEncoder encoder) throws AsnDecodingException {
        Object[] rVals = encoder.parseHeader(buf, offset);
        offset = (int) rVals[0];
        int cmd = (int) rVals[1];
        int length = (int) rVals[2];
        int begin = offset;
        if (cmd < 0) {
            cmd += 256;
        }
        this.m_command = cmd;
        final SnmpInt32 val = new SnmpInt32();
        offset = val.decodeASN(buf, offset, encoder);
        this.m_requestId = val.getValue();
        offset = val.decodeASN(buf, offset, encoder);
        this.m_errStatus = val.getValue();
        offset = val.decodeASN(buf, offset, encoder);
        this.m_errIndex = val.getValue();
        rVals = encoder.parseHeader(buf, offset);
        offset = (int) rVals[0];
        length = (int) rVals[2];
        final byte asnType = (byte) rVals[1];
        if (asnType != 48) {
            throw new AsnDecodingException("Invalid SNMP variable list");
        }
        begin = offset;
        this.m_variables.clear();
        final SnmpVarBind vb = new SnmpVarBind();
        while (length > 0) {
            offset = vb.decodeASN(buf, offset, encoder);
            length -= offset - begin;
            begin = offset;
            this.m_variables.add(vb.duplicate());
        }
        return offset;
    }

    public abstract SnmpSyntax duplicate();

    public abstract Object clone();

    static {
        SnmpPduPacket.sm_seq = 0;
    }
}
